package opentracing

import (
	"reflect"
	"fmt"
)

type FuncReflectWalk func(prefix, tag string, field interface{}, kind uint)  error

func reflectTags(msg interface{}, tagName string, prefix string, funWalk FuncReflectWalk) {

	var keyPrefix = ""
	if 0 < len(prefix){
		keyPrefix = prefix + "."
	}

	v := reflect.ValueOf(msg)
	// Only deal with pointers to structs.

	if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
		return
	}
	// Deref the pointer get to the struct.
	v = v.Elem()
	t := v.Type()
	for i := 0; i < v.NumField(); i++ {
		field := v.Field(i)
		kind := field.Kind()

		// Only recurse down direct pointers, which should only be to nested structs.
		if (kind == reflect.Ptr || kind == reflect.Interface) && field.CanInterface() {

			if tag := t.Field(i).Tag.Get(tagName); tag != "" {

				key := keyPrefix + tag
				reflectTags(field.Interface(), tagName, key, funWalk)
			}

		}
		// In case of arrays/slices (repeated fields) go down to the concrete type.
		if kind == reflect.Array || kind == reflect.Slice {
			flen := field.Len()
			if flen == 0 {
				continue
			}

			//kind = field.Index(0).Kind()
			if tag := t.Field(i).Tag.Get(tagName); tag != "" {
				//fmt.Println("arrayTag", tag)
				for pos := 0; pos < flen; pos++ {
					key:= fmt.Sprintf("%s%s[%d]", keyPrefix,tag,pos)
					reflectTags(field.Index(pos).Interface(), tagName, key, funWalk)
				}
			}
		}

		// Only be interested in
		if (kind >= reflect.Bool && kind <= reflect.Float64) || kind == reflect.String {

			if key := t.Field(i).Tag.Get(tagName); key != "" {
				//fmt.Println("key", key)
				//existingMap[keyPrefix + tag] = field.Interface()
				funWalk(keyPrefix, key, field.Interface(), uint(kind))
			}
		}
	}
	return

}
